// 步骤 4: 定义 AST 节点
use crate::ast::bin_op::BinOp;
use crate::ast::source_location::SourceLocation;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};

/// 表示函数参数的结构
#[derive(Debug, Clone)]
pub struct Param {
    pub name: String,
    pub type_name: Option<String>, // 参数类型标注，如"文"表示字符串类型
    pub default_value: Option<Box<ASTNode>>, // 可选参数的默认值
    pub is_varargs: bool,          // 是否为可变参数
    pub location: Option<SourceLocation>,
}

impl PartialEq for Param {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name && self.is_varargs == other.is_varargs
    }
}

impl Eq for Param {}

impl Hash for Param {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.name.hash(state);
        self.is_varargs.hash(state);
    }
}

/// 表示函数调用参数的枚举
#[derive(Debug, Clone, PartialEq)]
pub enum Argument {
    /// 位置参数
    Positional(ASTNode, Option<SourceLocation>),
    /// 关键字参数
    Keyword(String, ASTNode, Option<SourceLocation>), // (参数名, 值, 位置)
}

/// 表示字符串插值中的部分
#[derive(Debug, Clone, PartialEq)]
pub enum StringInterpolationPart {
    /// 普通文本部分
    Text(String),
    /// 插值表达式部分
    Expression(ASTNode),
}

#[derive(Debug, Clone, PartialEq)]
pub enum ASTNode {
    Program {
        preamble: Vec<ASTNode>,
        body: Vec<ASTNode>,
        epilogue: Vec<ASTNode>,
        location: Option<SourceLocation>,
    },
    Import {
        path: String,
        symbols: Option<Vec<String>>, // None = 导入全部, Some(...) = 导入指定符号
        location: Option<SourceLocation>,
    },

    Chapter {
        name: String,
        intro: Vec<ASTNode>,
        body: Vec<ASTNode>,
        conclusion: Vec<ASTNode>,
        location: Option<SourceLocation>,
    },

    ConstDeclaration {
        name: String,
        type_name: Option<String>, // 常量类型标注
        value: Box<ASTNode>,
        location: Option<SourceLocation>,
    },

    VarDeclaration {
        name: String,
        type_name: Option<String>, // 变量类型标注
        value: Box<ASTNode>,
        location: Option<SourceLocation>,
    },

    Assignment {
        name: String,
        value: Box<ASTNode>,
        location: Option<SourceLocation>,
    },

    FunctionDef {
        name: String,
        return_type: Option<String>, // 返回值类型标注，如"数"表示数字类型
        params: Vec<Param>,
        body: Vec<ASTNode>,
        location: Option<SourceLocation>,
    },

    FunctionCall {
        name: String,
        args: Vec<Argument>,
        location: Option<SourceLocation>,
    },

    If {
        condition: Box<ASTNode>,
        then_block: Vec<ASTNode>,
        else_if_conditions: Vec<Box<ASTNode>>, // elseif条件列表
        else_if_blocks: Vec<Vec<ASTNode>>,     // elseif对应的代码块
        else_block: Option<Vec<ASTNode>>,      // 最后的else块
        location: Option<SourceLocation>,
    },

    While {
        condition: Box<ASTNode>,
        body: Vec<ASTNode>,
        location: Option<SourceLocation>,
    },

    DoWhile {
        body: Vec<ASTNode>,
        condition: Box<ASTNode>,
        location: Option<SourceLocation>,
    },

    ForEach {
        variable: String,
        iterable: Box<ASTNode>,
        body: Vec<ASTNode>,
        location: Option<SourceLocation>,
    },

    ForDictEach {
        key_var: String,
        value_var: String,
        iterable: Box<ASTNode>,
        body: Vec<ASTNode>,
        location: Option<SourceLocation>,
    },

    ForRange {
        variable: String,
        start: Box<ASTNode>,
        end: Box<ASTNode>,
        step: Option<Box<ASTNode>>,
        body: Vec<ASTNode>,
        location: Option<SourceLocation>,
    },

    Return(Option<Box<ASTNode>>, Option<SourceLocation>),
    Export(Vec<String>, Option<SourceLocation>),
    Break(Option<SourceLocation>),
    Continue(Option<SourceLocation>),

    BinaryOp {
        left: Box<ASTNode>,
        op: BinOp,
        right: Box<ASTNode>,
        location: Option<SourceLocation>,
    },

    UnaryOp {
        op: String,
        operand: Box<ASTNode>,
        location: Option<SourceLocation>,
    },

    Integer(i64, Option<SourceLocation>),
    Float(f64, Option<SourceLocation>),
    #[allow(dead_code)]
    Number(f64, Option<SourceLocation>),
    String(String, Option<SourceLocation>),
    StringInterpolation {
        parts: Vec<StringInterpolationPart>,
        location: Option<SourceLocation>,
    },
    Boolean(bool, Option<SourceLocation>),

    #[allow(dead_code)]
    Array(Vec<ASTNode>, Option<SourceLocation>),
    #[allow(dead_code)]
    ArrayAccess {
        array: Box<ASTNode>,
        index: Box<ASTNode>,
        location: Option<SourceLocation>,
    },
    ArrayPush {
        array: Box<ASTNode>,
        value: Box<ASTNode>,
        location: Option<SourceLocation>,
    },

    Null(Option<SourceLocation>),
    List(Vec<ASTNode>, Option<SourceLocation>),
    Set(Vec<ASTNode>, Option<SourceLocation>),
    Dict(Vec<(ASTNode, ASTNode)>, Option<SourceLocation>),

    Identifier(String, Option<SourceLocation>),

    // 类相关节点
    ClassDef {
        name: String,
        fields: Vec<(String, Option<ASTNode>)>, // (字段名, 初始值)
        methods: HashMap<String, (Vec<String>, Vec<ASTNode>)>, // 方法名 -> (参数, 函数体)
        location: Option<SourceLocation>,
    },

    MemberAccess {
        object: Box<ASTNode>, // 对象表达式（可以是 Identifier("其") 表示 self）
        field: String,
        location: Option<SourceLocation>,
    },

    MemberAssign {
        object: Box<ASTNode>,
        field: String,
        value: Box<ASTNode>,
        location: Option<SourceLocation>,
    },

    MethodCall {
        object: Box<ASTNode>,
        method: String,
        args: Vec<ASTNode>,
        location: Option<SourceLocation>,
    },

    Print(Vec<ASTNode>, bool, Option<SourceLocation>),
}

/// 为Param结构体实现的辅助方法
impl Param {
    pub fn get_line(&self) -> usize {
        self.location.as_ref().map(|l| l.line).unwrap_or(0)
    }

    pub fn get_column(&self) -> usize {
        self.location.as_ref().map(|l| l.column).unwrap_or(0)
    }
}

/// 为Argument枚举实现的辅助方法
impl Argument {
    pub fn get_line(&self) -> usize {
        match self {
            Argument::Positional(_, location) | Argument::Keyword(_, _, location) => {
                location.as_ref().map(|l| l.line).unwrap_or(0)
            }
        }
    }

    pub fn get_column(&self) -> usize {
        match self {
            Argument::Positional(_, location) | Argument::Keyword(_, _, location) => {
                location.as_ref().map(|l| l.column).unwrap_or(0)
            }
        }
    }
}

impl ASTNode {
    pub fn get_line(&self) -> usize {
        match self {
            ASTNode::Program { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::Import { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::Chapter { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::ConstDeclaration { location, .. } => {
                location.as_ref().map(|l| l.line).unwrap_or(0)
            }
            ASTNode::VarDeclaration { location, .. } => {
                location.as_ref().map(|l| l.line).unwrap_or(0)
            }
            ASTNode::Assignment { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::FunctionDef { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::FunctionCall { location, .. } => {
                location.as_ref().map(|l| l.line).unwrap_or(0)
            }
            ASTNode::If { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::While { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::DoWhile { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::ForEach { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::ForDictEach { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::ForRange { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::Return(_, location) => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::Export(_, location) => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::Break(location) => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::Continue(location) => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::BinaryOp { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::UnaryOp { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::Integer(_, location) => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::Float(_, location) => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::Number(_, location) => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::String(_, location) => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::StringInterpolation { location, .. } => {
                location.as_ref().map(|l| l.line).unwrap_or(0)
            }
            ASTNode::Boolean(_, location) => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::Array(_, location) => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::ArrayAccess { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::ArrayPush { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::Null(location) => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::List(_, location) => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::Set(_, location) => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::Dict(_, location) => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::Identifier(_, location) => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::ClassDef { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::MemberAccess { location, .. } => {
                location.as_ref().map(|l| l.line).unwrap_or(0)
            }
            ASTNode::MemberAssign { location, .. } => {
                location.as_ref().map(|l| l.line).unwrap_or(0)
            }
            ASTNode::MethodCall { location, .. } => location.as_ref().map(|l| l.line).unwrap_or(0),
            ASTNode::Print(_, _, location) => location.as_ref().map(|l| l.line).unwrap_or(0),
        }
    }

    pub fn get_column(&self) -> usize {
        match self {
            ASTNode::Program { location, .. } => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::Import { location, .. } => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::Chapter { location, .. } => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::ConstDeclaration { location, .. } => {
                location.as_ref().map(|l| l.column).unwrap_or(0)
            }
            ASTNode::VarDeclaration { location, .. } => {
                location.as_ref().map(|l| l.column).unwrap_or(0)
            }
            ASTNode::Assignment { location, .. } => {
                location.as_ref().map(|l| l.column).unwrap_or(0)
            }
            ASTNode::FunctionDef { location, .. } => {
                location.as_ref().map(|l| l.column).unwrap_or(0)
            }
            ASTNode::FunctionCall { location, .. } => {
                location.as_ref().map(|l| l.column).unwrap_or(0)
            }
            ASTNode::If { location, .. } => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::While { location, .. } => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::DoWhile { location, .. } => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::ForEach { location, .. } => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::ForDictEach { location, .. } => {
                location.as_ref().map(|l| l.column).unwrap_or(0)
            }
            ASTNode::ForRange { location, .. } => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::Return(_, location) => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::Export(_, location) => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::Break(location) => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::Continue(location) => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::BinaryOp { location, .. } => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::UnaryOp { location, .. } => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::Integer(_, location) => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::Float(_, location) => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::Number(_, location) => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::String(_, location) => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::StringInterpolation { location, .. } => {
                location.as_ref().map(|l| l.column).unwrap_or(0)
            }
            ASTNode::Boolean(_, location) => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::Array(_, location) => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::ArrayAccess { location, .. } => {
                location.as_ref().map(|l| l.column).unwrap_or(0)
            }
            ASTNode::ArrayPush { location, .. } => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::Null(location) => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::List(_, location) => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::Set(_, location) => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::Dict(_, location) => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::Identifier(_, location) => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::ClassDef { location, .. } => location.as_ref().map(|l| l.column).unwrap_or(0),
            ASTNode::MemberAccess { location, .. } => {
                location.as_ref().map(|l| l.column).unwrap_or(0)
            }
            ASTNode::MemberAssign { location, .. } => {
                location.as_ref().map(|l| l.column).unwrap_or(0)
            }
            ASTNode::MethodCall { location, .. } => {
                location.as_ref().map(|l| l.column).unwrap_or(0)
            }
            ASTNode::Print(_, _, location) => location.as_ref().map(|l| l.column).unwrap_or(0),
        }
    }
}
